package cn.itsource.ymcc.service.impl;

import cn.itsource.ymcc.constant.ErrorConstantCode;
import cn.itsource.ymcc.constant.RedisConstants;
import cn.itsource.ymcc.domain.Course;
import cn.itsource.ymcc.domain.KillActivity;
import cn.itsource.ymcc.domain.KillCourse;
import cn.itsource.ymcc.dto.AddKillCourseDto;
import cn.itsource.ymcc.dto.KillDto;
import cn.itsource.ymcc.dto.PreOrderDto;
import cn.itsource.ymcc.exception.GlobalException;
import cn.itsource.ymcc.feignclient.CourseFeignClient;
import cn.itsource.ymcc.mapper.KillCourseMapper;
import cn.itsource.ymcc.result.JSONResult;
import cn.itsource.ymcc.service.IKillActivityService;
import cn.itsource.ymcc.service.IKillCourseService;
import cn.itsource.ymcc.util.AssertUtil;
import cn.itsource.ymcc.util.CodeGenerateUtils;
import cn.itsource.ymcc.util.SecurityContextUtil;
import cn.itsource.ymcc.util.StrUtils;
import cn.itsource.ymcc.vo.CourseDetailVO;
import cn.itsource.ymcc.vo.CourseOrderDetailVo;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author gwk
 * @since 2023-03-17
 */
@Service
@Slf4j
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService activityService;

    @Autowired
    private CourseFeignClient courseFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 新增秒杀活动的课程
     * @param killCoursedto
     */
    @Override
    public void save(AddKillCourseDto killCoursedto) {
        //1.参数校验 --jsr303
        Long courseId = killCoursedto.getCourseId();
        Long activityId = killCoursedto.getActivityId();

        //2. 业务校验
        // 2.2 活动是否存在，活动已经开始，或者已经结束，是不能添加的
        KillActivity killActivity = activityService.selectById(activityId);
        AssertUtil.isNotNull(killActivity, ErrorConstantCode.PARAMETER_NOT_BE_NULL);
        Date endTime = killActivity.getEndTime();
        Date beginTime = killActivity.getBeginTime();
        valiTime(beginTime,endTime);

        // 2.1 课程是否存在，是否上线的
        JSONResult jsonResult = courseFeignClient.getDetail(courseId);
        log.info("调用课程服务，获取课程信息：{}",jsonResult.getData().toString().substring(0,200));
        AssertUtil.isTrue(jsonResult.isSuccess(),ErrorConstantCode.COURSE_ERROR_30010);

        CourseDetailVO courseDetailVO = JSON.parseObject(JSON.toJSONString(jsonResult.getData()),CourseDetailVO.class);
        AssertUtil.isNotNull(courseDetailVO,ErrorConstantCode.COURSE_ERROR_30010);
        Course course = courseDetailVO.getCourse();
        AssertUtil.isTrue(Course.COURSE_ONLINE.equals(course.getStatus()),ErrorConstantCode.COURSE_NOT_ONLINE_30060);

        //3. 保存数据 killCourse
        KillCourse killCourse = new KillCourse();
        killCourse.setCourseName(course.getName());
        killCourse.setCourseId(course.getId());
        killCourse.setKillPrice(killCoursedto.getKillPrice());
        killCourse.setKillCount(killCoursedto.getKillCount());
        killCourse.setPublishStatus(0); // 0 待发布
        killCourse.setCoursePic(course.getPic()); // 0 待发布
        killCourse.setStartTime(beginTime);
        killCourse.setEndTime(endTime);
        killCourse.setCreateTime(new Date());
        killCourse.setTeacherNames(course.getTeacherNames());
        killCourse.setActivityId(activityId);
        killCourse.setTimeStr(killActivity.getTimeStr());

        log.info("保存秒杀课程信息：{}",killCourse);
        insert(killCourse);


    }

    /**
     * 查询需要发布的秒课程
     * @return
     */
    @Override
    public List<KillCourse> getShuldKillCourse() {

        return baseMapper.getShuldKillCourse();

    }

    /**
     * 到redis中查询所有上架商品的集合
     * @return
     */
    @Override
    public List<KillCourse> onlineAll() {

        List values = redisTemplate.boundHashOps(RedisConstants.KILL_ACTIVITY_KEY).values();
        log.info("Reids查询上架商品：{}",values);
        return values;
    }

    @Override
    public KillCourse onlineOne(String killId) {
        KillCourse killCourse = (KillCourse)redisTemplate.boundHashOps(RedisConstants.KILL_ACTIVITY_KEY).get(killId);
        log.info("Reids查询上架商品：{}",killCourse);
        return killCourse;
    }

    /**
     * 秒杀下单校验
     * @param killDto
     * @return
     */
    @Override
    public String kill(KillDto killDto) {
        // 1. 参数校验- jsr303
        String killCourseId = killDto.getKillCourseId();
        String killCode = killDto.getKillCode();
        //TODO 先写固定的用户信息
       Long loginId = SecurityContextUtil.getUserDetail().getUserId();
        //2. 1 一个用户，一个商品只能购买一次 -->  秒杀商品Id:用户id   redis
       String rediskey = loginId + ":" + killCourseId;
       Object obj = redisTemplate.opsForValue().get(rediskey);
       AssertUtil.isNull(obj,ErrorConstantCode.REPEAT_SECKILL_60010);

        //2.2 秒杀码和秒杀商品是否匹配 -- 根据id 到redis查询 killcode
        KillCourse killCourse = (KillCourse)redisTemplate.boundHashOps(RedisConstants.KILL_ACTIVITY_KEY).get(killCourseId);
        AssertUtil.isTrue(killCode.equals(killCourse.getKillCode()),ErrorConstantCode.PARAMETER_NOT_BE_NULL);

        //2.3 秒杀的商品，是否在活动期间
        AssertUtil.isTrue(killCourse.getKilling(),ErrorConstantCode.PARAMETER_NOT_BE_NULL);

        //2.4 信号量判断商品库存 根据id
        RSemaphore semaphore = redissonClient.getSemaphore("killId:" + killCourseId);
        Boolean b = semaphore.tryAcquire(1);
        if (b.equals(false)){
            throw new GlobalException("秒杀商品库存不足，请稍后重试");
        }


        //3 下单流程：
        //kill-server 预创订单 --> 保存到redis
        PreOrderDto preOrderDto = new PreOrderDto();
        preOrderDto.setCourseName(killCourse.getCourseName());
        preOrderDto.setCoursePic(killCourse.getCoursePic());
        preOrderDto.setCreateTime(new Date());
        preOrderDto.setKillCourseId(killCourse.getId());
        String orderNO = CodeGenerateUtils.generateUnionPaySn();
        preOrderDto.setOrderNo(orderNO);
        preOrderDto.setStatusOrder(PreOrderDto.NO_PAY);
        String titile = String.format("购买课程 [%s] , 支付[%s] 元",killCourse.getCourseName(),killCourse.getKillPrice());
        preOrderDto.setTitle(titile);
        preOrderDto.setTotalAmount(killCourse.getKillPrice());
        preOrderDto.setTotalCount(1);
        preOrderDto.setUserId(loginId);

        //大订单和订单项
        redisTemplate.opsForValue().set(orderNO,preOrderDto);


        redisTemplate.opsForValue().set(rediskey,"已购买");

        //把订单编号 orderNo 返回给用户
        return orderNO;
    }

    //业务校验： 开始时间晚与当前时间， 结束时间晚于开始时间
    public void valiTime (Date beginTime, Date endTime){
        Date now = new Date();

        if(now.getTime() > beginTime.getTime() ){
            throw  new GlobalException("开始时间应该，晚与当前时间");
        }

        if(endTime.getTime() < beginTime.getTime() ){
            throw  new GlobalException("结束时间应该，晚于开始时间");
        }

        if(endTime.getTime() < now.getTime() ){
            throw  new GlobalException("结束时间应该，晚于当前时间");
        }
    }
}
